/*
 * Copyright (c) xiake6. All Rights Reserved.
 * @Owner:fenglibin
 * @Date:  2017-10-16 15:04
 */
package net.xiake6.springcloud.gateway.filter;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.apache.skywalking.apm.toolkit.trace.TraceContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.http.HttpStatus;

import com.google.common.base.Strings;
import com.netflix.client.http.HttpResponse;
import com.netflix.util.Pair;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;

import net.xiake6.springcloud.gateway.common.Constants;
import net.xiake6.springcloud.gateway.common.StringUtil;

/**
 * 统一参数记录.
 *
 * @author: fenglibin
 * @date: 2017-10-16 15:04
 */
public class PostFilter extends ZuulFilter {

    private static Logger logger = LoggerFactory.getLogger(PostFilter.class);


    @Override
    public String filterType() {
        return FilterConstants.POST_TYPE;
    }

    @Override
    public int filterOrder() {
        return 2;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        String traceId = TraceContext.traceId();
        RequestContext ctx = RequestContext.getCurrentContext();
        ctx.addZuulResponseHeader(Constants.Expose_Headers, Constants.TRACE_ID);
        ctx.addZuulResponseHeader(Constants.TRACE_ID, traceId);
        try {
            afterInvokeService(ctx);
        } catch (Exception ex) {
            logger.error("PostFilter Error", ex);
        } finally {

        }
        return null;
    }


    /**
     * 统一记录请求输入输出参数. logstash grok: \^\_\^%{DATA:requestTime}\^\_\^%{DATA:responseTime}\^\_\^%{DATA:duringTime}\^\_\^%{DATA:serviceId}
     * \^\_\^%{IP:serviceIp}\^\_\^%{DATA:servicePort}\^\_\^%{IP:clientIp}\^\_\^%{DATA:device}\^\_\^%{DATA:userId}
     * \^\_\^%{DATA:authorization}\^\_\^%{DATA:resCode}\^\_\^%{DATA:requestURL}\^\_\^%{DATA:requestURI}
     * \^\_\^%{DATA:referer}\^\_\^%{DATA:version}\^\_\^%{DATA:queryString}\^\_\^%{DATA:queryBody}
     * \^\_\^%{DATA:resData}\^\_\^%{DATA:errorMsg}\^\_\^%{DATA:throwable}\^\_\^
     */
    private void afterInvokeService(RequestContext ctx) {
        String responseData = "";
        String httpCode = ctx.get(Constants.RESPONSE_STATUS_CODE) == null ? ""
                : ctx.get(Constants.RESPONSE_STATUS_CODE).toString();

        String conternTpye = getContentType(ctx);
        if (!conternTpye.contains("json")) {
            responseData = conternTpye;
        } else if (!Strings.isNullOrEmpty(httpCode) && !httpCode.equals(HttpStatus.OK.value())) {
            try {
                InputStream inputStream = RequestContext.getCurrentContext().getResponseDataStream();
                if (inputStream != null) {
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    byte[] buffer = new byte[1024];
                    int length;
                    while ((length = inputStream.read(buffer)) != -1) {
                        bos.write(buffer, 0, length);
                    }
                    bos.flush();
                    InputStream originalStream = new ByteArrayInputStream(bos.toByteArray());
                    RequestContext.getCurrentContext().setResponseDataStream(originalStream);
                    responseData = bos.toString(StandardCharsets.UTF_8.name());
                } else {
                    responseData = ctx.get(Constants.RESPONSE_BODY) == null ? ""
                            : ctx.get(Constants.RESPONSE_BODY).toString();
                }
            } catch (Exception ex) {
                logger.error("parse InputStream error:", ex);
            } finally {
            }
        }


        HttpServletRequest request = ctx.getRequest();

        long currentDate = System.currentTimeMillis();

        long requestTime =
                ctx.get(Constants.REQUEST_TIME) == null ? currentDate : (long) ctx.get(Constants.REQUEST_TIME);

        HttpResponse ribbonrsp = ((HttpResponse) ctx.get(Constants.RIBBON_RESPONSE));

        String serviceIp =
                ribbonrsp == null ? request.getLocalAddr() : ribbonrsp.getRequestedURI().getHost();

        int servicePort =
                ribbonrsp == null ? request.getLocalPort() : ribbonrsp.getRequestedURI().getPort();


        long duringTime = currentDate - requestTime;

        String traceId = TraceContext.traceId();

        String requestPatam = getParameter(request);
        String requestBody = getRequestPostStr(request);
        String clientIp = StringUtil.getIpAddr(request);
        String userId = ctx.get(Constants.HEADER_USER_KEY) == null ? ""
                : ctx.get(Constants.HEADER_USER_KEY).toString();
        String token = request.getHeader(Constants.QUERY_TOKEN_KEY);
        if (Strings.isNullOrEmpty(token)) {
            token = request.getParameter(Constants.QUERY_TOKEN_KEY);
        }

        StringBuilder sb = new StringBuilder();
        sb.append(Constants.LOG_SPLITER);
        sb.append(traceId).append(Constants.LOG_SPLITER);
        sb.append(requestTime).append(Constants.LOG_SPLITER);
        sb.append(currentDate).append(Constants.LOG_SPLITER);
        sb.append(duringTime).append(Constants.LOG_SPLITER);
        sb.append(ctx.get(Constants.SERVICE_ID)).append(Constants.LOG_SPLITER);
        sb.append(serviceIp).append(Constants.LOG_SPLITER);
        sb.append(servicePort).append(Constants.LOG_SPLITER);
        sb.append(clientIp).append(Constants.LOG_SPLITER);
        sb.append(request.getHeader(Constants.REQUEST_USER_AGENT)).append(Constants.LOG_SPLITER);
        sb.append(userId).append(Constants.LOG_SPLITER);
        sb.append(token).append(Constants.LOG_SPLITER);
        sb.append(ctx.get(Constants.RESPONSE_STATUS_CODE)).append(Constants.LOG_SPLITER);
        sb.append(request.getRequestURL()).append(Constants.LOG_SPLITER);
        sb.append(request.getHeader(Constants.REQUEST_REFERER)).append(Constants.LOG_SPLITER);
        sb.append(requestPatam).append(Constants.LOG_SPLITER);
        sb.append(requestBody).append(Constants.LOG_SPLITER);
        sb.append(responseData).append(Constants.LOG_SPLITER);
        logger.info(sb.toString());

//        tracer.addTag("requestParam", requestPatam);
//        tracer.addTag("requestBody", requestBody);
//        tracer.addTag("requestResult", responseData);
//        tracer.addTag("service-id", ctx.get("serviceId") == null ? "" : ctx.get("serviceId").toString());
//        tracer.addTag("service-ip", serviceIp);
//        tracer.addTag("service-port", String.valueOf(servicePort));
//        tracer.addTag("clinet-ip", clientIp);
//        tracer.addTag("user-agent", request.getHeader("user-agent"));
//        tracer.addTag("referer", request.getHeader("Referer"));
//        tracer.addTag("erp-user-id", userId);
//        tracer.addTag("erp-auth-token", token);

    }


    public byte[] getRequestPostBytes(HttpServletRequest request)
            throws IOException {
        int contentLength = request.getContentLength();
        if (contentLength < 0) {
            return null;
        }
        byte[] buffer = new byte[contentLength];
        for (int i = 0; i < contentLength; ) {

            int readlen = request.getInputStream().read(buffer, i,
                    contentLength - i);
            if (readlen == -1) {
                break;
            }
            i += readlen;
        }
        return buffer;
    }


    /**
     * 获取请求体中的字符串(POST)
     */
    public String getRequestPostStr(HttpServletRequest request) {
        try {
            if (request.getContentType().contains("form-data")) {
                return "form-data";
            }

            byte[] buffer = getRequestPostBytes(request);
            String charEncoding = request.getCharacterEncoding();
            if (charEncoding == null) {
                charEncoding = "UTF-8";
            }
            return new String(buffer, charEncoding);
        } catch (Exception ex) {
            return "";
        }


    }


    private String getParameter(HttpServletRequest request) {
        StringBuilder sb = new StringBuilder();
        Enumeration em = request.getParameterNames();
        while (em.hasMoreElements()) {
            String key = (String) em.nextElement();
            sb.append(key + "=" + request.getParameter(key) + "&");

        }
        return sb.toString();

    }

    private String getContentType(RequestContext context) {
        List<Pair<String, String>> headers = context.getZuulResponseHeaders();
        String contentType = "";
        for (Pair<String, String> header : headers) {
            if (header.first().toLowerCase().equals("content-type")) {
                contentType = header.second();
                break;
            }
        }
        return contentType;
    }

    private String getHeader(HttpServletRequest request) {
        StringBuilder sb = new StringBuilder();
        Enumeration headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String key = (String) headerNames.nextElement();
            sb.append(key + "=" + request.getHeader(key)).append("\n");
        }
        return sb.toString();
    }
}

